/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

import buildURL from "../../node_modules/axios/lib/helpers/buildURL"
import buildFullPath from "../../node_modules/axios/lib/core/buildFullPath"
import settle from "../../node_modules/axios/lib/core/settle"
import AxiosError from '../../node_modules/axios/lib/core/AxiosError'
import request from '@ohos.request';
import fileio from '@ohos.fileio';

/**
 * 获取 file 和 data
 * 1.如果是uri，直接从config.data中取值并且返回
 * 2.如果是ArrayBuffer，存储到本地临时目录并且返回路径
 * 3.如果是字符，存储到本地临时目录并且返回路径
 * @param requestData
 * @param reject
 * @param context
 */
function getFileList(requestData, reject, cacheDir){

    let files = []
    let data = []

    requestData.forEach((value, key, option)=>{
        if (typeof(value) === 'string' && (value.indexOf('internal://') == 0 || value.indexOf('dataability://') == 0)) { // uri
            files.push({
                filename: getFileNameByPath(value) || '',
                name: key,
                uri: value,
                type: ''
            })
        }
        else if (value instanceof ArrayBuffer) { // ArrayBuffer
            //  fileName检验
            if(!option || !(typeof option == 'string' || (option instanceof Object && option.fileName))) {
                clean(cacheDir)
                reject(new AxiosError('The FormData object must fill in the option parameter!', AxiosError.ERR_BAD_OPTION, null, null, null));
            }

            // 将arrayBuffer暂时保存至本地目录
            let path = `${cacheDir}/axios_temp`;
            // axios_temp目录不存在则创建
            try {
                fileio.accessSync(path);
            } catch(err) {
                fileio.mkdirSync(path);
            }

            let name = typeof(option) === 'string' ? option : option.fileName
            let fd = fileio.openSync(path + '/' + name, 0o100 | 0o2, 0o666);
            let num = fileio.writeSync(fd, value);
            fileio.fsyncSync(fd);
            fileio.closeSync(fd);

            files.push({
                filename: getFileNameByPath(value) || '',
                name: key,
                uri: "internal://cache/axios_temp/" + name,
                type: ''
            })
        }else{
            data.push({
                name: key,
                value: value
            })
        }
    })

    return{
        files: files,
        data: data
    }
}

/**
 *  清理临时目录文件
 * @param options request配置项
 */
function clean(cacheDir, options ){
    let path_temp = cacheDir + '/axios_temp/'
    try{
        if(options){ //删除指定文件
            options&&options.files.forEach(item=>{
                item.uri && fileio.unlink(item.uri.replace('internal://cache/axios_temp/', path_temp))
            })
        }else { // 删除超过12小时的文件
            let dir = fileio.opendirSync(path_temp);
            let dirent = dir.readSync()
            let now = Date.now()
            while (dirent) {
                try {
                    let path = path_temp + '/' + dirent.name
                    let stat = fileio.statSync(path);
                    if (now - stat.atime >= 12 * 60 * 60 * 1000) {
                        fileio.unlink(path)
                    }
                    dirent = dir.readSync()
                } catch (err) {
                    dir.closeSync();
                    break
                }
            }
        }
    }catch(err){}
}

/**
 * 根据 已知文件路径，获取文件名xxx.doc
 */
function getFileNameByPath(path) {
    var index = path.lastIndexOf("/");
    var fileName = path.substr(index + 1);
    return fileName;
};

/**
 * 上传
 * @param config 配置项
 * @param resolve
 * @param reject
 */
async function upload(config, resolve, reject){

    let uploadTask ;
    let context = config.context
    let requestData = config.data;
    let fullPath = buildFullPath(config.baseURL, config.url);

    // 根据Stage模型和FA模型分别获取临时缓存目录
    let cacheDir = ''
    if(!context || !requestData ) reject(new AxiosError('Cannot read properties, please check the parameters!', AxiosError.ERR_BAD_OPTION, null, null, null));
    try {
        if (context.cacheDir) {
            cacheDir = context.cacheDir
        } else {
            let result = await context.getCacheDir()
            cacheDir = result ? result : reject(new AxiosError('Cannot read properties, please check the parameters!', AxiosError.ERR_BAD_OPTION, null, null, null));
        }
    }catch(err){
        reject(new AxiosError(err, AxiosError.ERR_BAD_OPTION, null, null, null));
    }
    // 每次上传前清理异常导致的残留文件
    clean(cacheDir)

    // 构建upload请求参数
    let list = getFileList(requestData, reject, cacheDir)

    let options = {
        url: buildURL(fullPath, config.params, config.paramsSerializer),
        header: config.header,
        method: config.method.toUpperCase(),
        files: list.files,
        data: list.data,
    }
    // 发送upload请求
    request.upload(context , options).then((data)=>{
        uploadTask = data
        if (typeof config.onUploadProgress === 'function') {
            uploadTask.on('progress', (loaded, total)=>{
                config.onUploadProgress({
                    loaded: loaded,
                    total: total
                })
            })

            // upload complete
            uploadTask.on('complete',(err)=>{
                let response = {
                    data: 'upload success!',
                    status: 200,
                    statusText: "",
                    headers: config.header,
                    config: config,
                    request: request
                };
                settle(function _resolve(value) {
                    resolve(value);
                }, function _reject(err) {
                    reject(err);
                }, response);
                clean(cacheDir, options)
            })
            // upload fail
            uploadTask.on('fail',(err)=>{
                let message = err && err[0] ? err[0].message : '';
                let code = err && err[0] ? err[0].responseCode : '';
                reject(new AxiosError(message, code, config, null, null));
            })
        }
    }).catch(error=>{
        reject(new AxiosError(error, AxiosError.ERR_NETWORK, config, null, null));
    })
}

export default upload
